"""
@-*- coding: utf-8 -*-
@ python：python 3.8
@ 创建人员:ylx-lingxiao
@ 创建时间:2024/11/5  23:10
@ File:study7.py
@ Explanation:Python如何操作文件
"""
"""

一：
打开文件
读写文件
关闭文件
在Python中使用open函数，可以打开一个已经存在的文件或者创建一个新的文件
open(name,mode)
name  就是我们打开的目标文件名称的字符串，可以包含文件所在的具体路径，相对路径或者绝对路径
mode 是你设置打开文件的模式，比如只读，写入，追加

1.打开文件的模式
r  只读方式打开文件，文件的指针放在文件的开头，这是默认模式
a 打开一个文件用于追加  若该文件存在，则指针放在文件的结尾。也就是说。新内容会被写到已有的内容之后，如果文件不存在
则创建新文件写入
w 打开一个文件只用于写入，如果该文件存在，则打开该文件从头开始编辑，既原有内容会被删除，如果该文件不存在，则创建新文件

体验
f = open('1.txt','w')

关闭
文件对象.close()
所以我们一定要有一个好的习惯，打开文件后，一定要先写关闭文件，然后中间才是你的操作，避免内存溢出

文件对象方法
写
对象.write('内容')

f = open('1.txt','w')  打开文件,如果有，那么覆盖写，如果没有，那么创建后从头开始写
f.write('hello world') 文件写入   这里要求写入的必须是字符串
f.close()

f = open('1.txt','w')  打开文件,如果有，那么覆盖写，如果没有，那么创建后从头开始写
f.write('hello world') 文件写入   这里要求写入的必须是字符串--连续写入
f.write('hello  world2')
f.close()

上述写法不换行，是在一行紧贴着输出，如果要换行则这样写
f = open('1.txt','w')  打开文件,如果有，那么覆盖写，如果没有，那么创建后从头开始写
f.write('hello world\n') 文件写入   这里要求写入的必须是字符串--连续写入
f.write('hello  world2')
f.close()

技巧，我们一般打开的都是其他地方的，所以我们使用绝对路径
那么绝对路径就会存在转义的情况，
要么前面加r 回归原始状态，要么都是双反斜杠\\取消转义

相对路径就在你这个目录下或者你当前目录的子级目录下均可以

如果你这里在改成汉字，那么写入是有可能乱码的，原因是不支持utf-8的编码，数字和英文不会有问题，我们解决的方案可以是在打开这个文件时
指定编码方式utf-8 完美兼容中文
f = open('1.txt','w',encoding='utf-8')


2.读
read()
文件对象.read(num)
num表示要从文件中读取的数据长度，如果没有传入num，那么就表示读取该文件夹中的所有的数据
不管中文汉字英文都按照1长度来算

readlines()  可以按照行的方式把整个文件的内容进行一次读取，返回一个列表，其中每一行数据为1个元素

readline() 一次读取一行的内容


f = open('1.txt','w')  打开文件,如果有，那么覆盖写，如果没有，那么创建后从头开始写
f.write('大家好\n')
f.write('我是曼巴\n')
f.write('hello world3\n')
f.write('hello world4\n')
f.close()
f = open('1.txt','r',encoding = 'utf-8') 
print(f.read())  读到内容了，是有返回值的，用变量接或者直接打印即可  这个打印由于1.txt文件有5行，第五行为空行，所以print后展示2空行，print自己本身也会换行一次
print(f.read(3))  读的就是  大家好
print(f.read(5)) 从第一个长度开始读，读取5个长度  因为print(len('大家好\n我')) 这个长度就是5，换行符也是要读的，实际来说换行符也是要算的
打印:
大家好
我

print(f.readlines().strip()) 读取全部的行，返回一个列表
print(f.readline().strip())  仅读一行，可以调用多次

我们针对上面读出来来的数据，最好的处理方式是使用strip()函数，来处理字符串前后的空格和换行符
f.close()



f = open('1.txt','w')  打开文件,如果有，那么覆盖写，如果没有，那么创建后从头开始写
f.write('大家好\n')
f.write('我是曼巴\n')
f.write('今天是周日\n')
f.write('我在讲Python')
f.close()
f = open('1.txt','r',encoding = 'utf-8') 
print(f.read(5))  
print('-----')
print(f.readline())  
print('-----')
print(f.readlines())
f.close()

打印
大家好
我
是曼巴

['今天是周日\n','我在讲Python']

这个例子说明所有的读都是依赖指针的，指针在哪里，我们只能读指针后面的内容
也就是说你上来就read下，那你后续的读就没办法读了，因为指针已经去了最后

在比如这样的例子
f = open('1.txt','w')  打开文件,如果有，那么覆盖写，如果没有，那么创建后从头开始写
f.write('大家好\n')
f.write('我是曼巴\n')
f.write('今天是周日\n')
f.write('我在讲Python')
f.close()
f = open('1.txt','r',encoding = 'utf-8') 
print(f.readlines())
for line in f.readlines():
    print(line)         这里是没有输出的，因为第一个print已经readlines过了，指针到了最后，再次读取的实惠是无内容的
    
处理方案是去掉第一个print，直接开始for循环，并且strip函数来处理字符串的前后转义和空格
for line in f.readlines():
    print(line.strip())    strip()函数可以去掉开头和结尾的换行符以及空格
    
第二个方法是既然第一次都已经readlines了
那就
aa = f.readlines()
for line in aa:
    print(line.strip())
    
    
f.close()




with open  
是Python用来打开文件的，会在使用完后自动关闭文件,这个写法就是上面复用了原来的 open，然后缩进里边写，原来open后f的一些操作，根据你打开文件的模式来写
with open(name,mode,encoding='utf-8') as f:
    data = f.write('hello')
    
    
    
    




二：
处理异常
当你的代码执行报错时，解释器无法执行，反而出现一些错误的提示，这就是所谓的异常
比如说我们
f = open('2.txt','r')  打开，只读模式一个不存在的文件，我们写的两种模式可以指定一个不存在的文件，因为写的时候如果不存在，会新建

处理异常的语法
try:
    可能发生错误的代码
    f = open('2.txt','r') 
except:
    如果出现异常，我们需要执行的代码-怎么处理这个异常
    
执行逻辑是先执行 try这个地方的代码，如果出错，则进入except模块，如果不出错，try里的代码执行是成功的，则继续执行try模块的，不会进入except
       
       
       
我们还可以捕获指定的异常
try:
    可能发生错误的代码
except 异常类型：
    如果捕获到我们要执行的代码
    
    
try:
    print(num)
except NameError:
    print('有错误')  
    
    
这里我们要注意，如果尝试执行的代码的异常，和我目标捕获的异常不一致，则无法捕获异常
一般来说，try下方只放一行尝试执行的代码




我们还可以捕获多个指定的异常
try:
    print(1/0)
except(NameError,FileNotFindError,ZeroDivisionError)    这个括号内可以写多个异常的名称，中间用逗号隔开，但是我们仅仅能捕获这个括号内提到的异常
    print('有错误')
    
    
一旦出现异常，try后面的就不执行了，所以我们通常建议try里仅仅只跟着1行代码




我们还可以讲捕获到的错误清晰的表达出来
try:
    print(1/0)
except(NameError,FileNotFindError,ZeroDivisionError) as result:  这里的写法就是，上面出现了异常我捕获这个异常后把这个详细的异常原因赋值给result这个变量
    print(result)
    
    
    
    
捕获所有的异常
try:
    print(num)
except Exception as result:   Exception 是Python所有异常的父类，找家长就能找到他下面的子类，孙子类等等
    print(result)







模块和包
Python的模块其实是一个Python文件，是以.py结尾的，包含了Python的对象定于和Python语句
模块能定义函数，类，变量，模块里也能包含可执行的代码

导入模块，可以实现模块之间的互通，同一个项目下，或者整个Python的公共模块
导入模块的方式  

import 模块名
或者 import 模块名1,模块名2...
调用功能
模块名.功能名()
比如
import math
print(math.sqrt(9))  打印 3.0    可以按住Ctrl 去点击对应的模块名称看具体的方法，进入这个模块或者.py文件
你导入后，你可以用它的函数，也可以用它里边的变量
比如 math.py文件中  pi = 3.1415926
那你导入后
print(math.pi)  就可以打印这个变量的值
你还可以引用模块的类然后实例化成为对象




from 模块名 import 功能名
from 模块名 import 功能名1,功能名2...
调用
功能名（）
from math import sqrt
print(sqrt(9)) 


from 模块名 import*
要里边所有的函数，类，变量




import 模块名 as 别名
from 模块名 import 功能名 as 别名




制作模块
在Python中每个Python文件都可以成为模块，模块的名字就是文件饿名字，就是说自定义模块必须要符合标识符命名规则  英文 数字 下划线  不能数字开头

定义模块 hello.py
def testa(a,b):
    print(a+b)
    

test(1,1)   执行当前这个文件 打印2




另外一个文件会引用hello.py这个文件
import hello

hello.testa(2,3)

结果就会输出


2
5


也就是说你调用第一个模块时，你不仅调用了里边的方法，还包括一些相关的调试代码也会执行
改法就是在你要导入的这个模块里加
定义模块 hello.py
def testa(a,b):
    print(a+b)
    
print(__name__,type(__name__))
if __name__ == '__main__':
test(1,1)   执行当前这个文件 打印2

另外一个文件会引用hello.py这个文件
import hello

hello.testa(2,3)
print(__name__,type(__name__))
结果就会输出
hello  str
5
__main__ str
将你自己要调试的全部放在这个后面，我们把我们引入的这个也叫程序的主入口，程序一般从这里开始执行，如果遇到要调用的，再回到最上面去调用相关的函数或者类或者变量
加这个的目的就是 ，你自己的模块你要调试的时候，你运行没有问题，别人要导入你的时候，别人运行，是不会执行你的测试代妈的
为什么能做到呢，因为Python中 谁是被执行的模块，谁的__name__ 就会被赋值字符串'__main__',否则就是自己的模块名




综上所述，你以后写代妈，类 函数 都在上面，你要调用这个类或者方法就在下面写
加了这个,你自己执行当前模块调试代妈会运行，别人调用，则不会
if __name__ = '__main__'


通过这个来区分你自己的测试用例你自己执行，别人要调用你的，方法或者类时，不会执行你的测试用例
就是为了区分导入别的模块时，不会执行这个模块下的调试代妈




你除了导入模块中的函数，还能导入模块中的类
比如说我们之前的washer()类

from test0713 import washer

if __name__ = '__main__':
    washer(500,800).fix()          你可以之间实例化这个类，然后不传给变量，而直接使用类中的方法，也支持
    




类里的方法可以单独定义参数
class grand_son(Prentice):
    def make_new_cake_2(self,num)
        print(f'hello,{num}')
        
        
 cc = grand_son()
 cc.make_new_cake_2(2)       
这样也就是说，如果这个参数，仅仅是这个方法需要的，你可以在实例化这个类后，调用这个方法时再传参，而如果是init要求的，那么你实例化对象时就要传参数
再补充就是方法中的形参，你规定了，你就必须差传进去，哪怕你不用
inint 方法中的属性，类的作用域内都可以用self.height = height
那么下个方法中 你就可以 self.height>500





包
包将有联系的模块组织在一起，放到同一个文件夹下，并且在这个文件夹创建一个名字为__init__.py文件，那么这个文件夹就被称之为包

制作包
项目，鼠标右键 new  之前我们是创建 new python file 这次我们可以选择python package   输入包名  OK 新建功能模块（有联系的模块）
比如说我们新建了一个mypackage的包，里边首先会自己生成一个__init__.py 文件

我们可以新建包内模块  my_module1   my_module2
简单来讲，包可以分类存放不同的模块，相同作用的放在一起

比如你之前引用
from module2 import *
现在module2.py放入 mypackage这个包里边了
那你就要改写为
from mypackage.module2 import *




导入包
import 包名.模块名
包名.模块名.目标

from 包名 import *   导入包下的所有模块
模块名.目标
解答-Python 导入包时
如果你的写法是
from mypackage import *
按理说你可以使用mypackage下的所有模块，比如说是modle1  modle2，并且使用这2模块中的方法  但是实际你要达到这样的效果，你需要在其下的__init__文件加这样一个
__all__ = ['你允许导入的模块'] 

加了这个后 你就可以,使用相关的模块下的方法了

modle1.test()
modle2.sayhello()





from 包名.模块名 import*
模块名.目标


也就是说调用的时候不加包名就是这2模块文件平级不用加，如果不，比如说你现在的模块和package一个层级，你要调用的是package里边的，那你就要按照上面的来写你的导入
也就是说如果你是包套包，那你的引用就必须按照是否在一个层级来写，不在的话就相当于是一个相对路径
from secondpackage.mypackage.test0713 import *


也就是说只要你们不在一个层级下，那么你调用这个模块时就要用相对路径，毕竟你们在一个项目下


层级结构如果是
mypackage下有secondpackage +__init__.py 文件+module1+module2   编辑__init__.py文件时

from .secondpackage.test0713 import washer





那么当你在和mypackage同级的py文件中
import mypackage




那么你在用的时候就可以直接
mypackage.washer(300,500).fix()

因为我们给mypackage 的初始化时 引入了一个类，所以导入mypackage时，就会有这个属性，所以我们就可以使用这个方法
这里就跟你实例化类一样，会先默认的去执行__init__方法中的相关操作，来给这个实例化的对象增加属性

这也就说明了我们使用Python的第三方库的时候 有时候我们只需要导入这个包名，即可使用包内的全部的方法，也是因为提前在__init__ 中给提前导入，也就是初始化了
所以我们才能更方便的去调用别人的方法













使用Python 操作数据库

pymysql

第一步  安装pymysql    pip install pymysql     cmd下执行  Windows命令行运行         pip uninstall  卸载

第二步  导入包

import pymysql

建立数据库连接
db = pymysql.connect(host='',user='',password='',port=)

建立游标对象，执行sql----这里可以理解为 你建立了连接，此时启一个虚拟的窗体来执行你每次的sql 这个游标对象会始终落在最新一次执行结果上
result = db.cursor()


执行sql

result.execute('sql语句')   注意里外的单双引号


上述的执行会有返回结果


6.获取最新一次的执行结果

data = result.fetchall()
print(data)

获取的结果每一行，作为一个元组，放进元组中去


最主要的还是你这个execute 执行sql语句，你可以写多行 result.execute('sql语句')，但是如果你要获取结果，你除非每个sql完了获取，才能获取每个sql
的执行结果，如果你只运行sql，最后获取结果，那你获取的就是最后一次执行的sql的  结果

再注意 数据库不能重复，所以你有的语句只能执行一次

比如说你先执行了
result.execute('create database bcbx') 
你想再次执行这个 肯定会报错，因为数据库不能重复
容错处理可以写成
result.execute('create database if not exists bcbx')   如果不存在 bcbx数据库 那就创建这个名称的数据库 

上面这个还能优化，你连接MySQL后还能指定访问哪个数据库
db = pymysql.connect(host='',user='',password='',port=,database='')



你使用如上，sql 建表  然后添加数据，Python中能查到，但是sql表中没有数据
原因是，第一次是预执行，Python内存中去数据库先预执行下，看sql是否有问题，需要真正执行的话，需要你提交一次

insert  update  delete   这三个sql语句需要你执行完后再提交一次   使用commit()

result.execute("insert into table1 values(1,'manba')")
具体使用是 db.commit()

这样数据库才会真正的执行这条sql，从而完成添加数据----这三种操作的sql，完了后都要commit一下，让数据库真正的执行




批量构造数据
比如说你一次性想给表中插入100行数据

try:
    for n in range(10):
        result.execute("insert into table1 values({n},manba{n})")
except:
    db.commit()                              这种就是把之前成功的提交了



下面这种
for n in range(10):
    result.execute("insert into table1 values({n},manba{n})")

db.commit()   统一预知执行成功后再提交，如果放在每执行一次就提交一次，那如果本次预执行失败了，那你就可能存在重复的数据，比如第一行 成功 插入进去 第二行失败 
无法插入，此时你再执行这个sql，就会存在第一行的数据重复问题





第一次执行  10条数据 成功1条  卡再第二条，再次执行又会出现重复问题

try:
    for n in range(10):
        result.execute("insert into table1 values({n},manba{n})")
    db.commit()
except:
    db.rollback()   ---回滚，上次修改未提交的全部清理掉，将你还没有提交的那些，从内存中执行通过的直径抹掉
    
    
全通过就提交，出现问题就回滚都最初样子












Python 操作 Excel
场景1  进行自动化测试时我们需要数据驱动，比如说 登录场景需要不同组合的账户密码进行验证，如果代码里写死  后期密码变更修改起来就比较麻烦
为了动态的获取数据，比如说密码变了，你只需要改Excel中的值就可以，而不需要全局大幅度的改动


场景2 测试一个文件的上传功能，可能需要Excel上万条数据，考虑使用Python快速构造大批量的Excel数据


读取Excel中的数据

pip install xlrd

这是Python的第三方包，意为xls文件的read库，主要用于读取Excel
可以实现指定表单，指定单元格的获取



先 
import xlrd

打开Excel文件读取数据
data = xlrd.open_workbook('1.xls')

使用技巧
table = data.sheet_by_index(0) 通过索引顺序获取
table = data.sheet_by_name('') 通过名称获取

获取整行 整列的值

table.row_values(0)   获取第一行的全部数据          打印 值 以列表的形式
table.col_values(0)   获取第一列的全部数据


获取行数 列数
nrows = table.nrows
ncols = table.ncols

循环行列表数据
for i in range(nrows):
    print(table.row_values(i))
    
单元格
table.cell(rowx,colx)

cell_1 = table.cell(0,0).value   获取值
cell_2 = table.cell(3,3).value

使用行列索引
cell_1 = table.row(0)[0].value
cell_2 = table.col(0)[0).value



（5，0）   第六行 第一列
（0，0）  第一行 第一列




覆盖方式去写Excel

pip install xlwt
xlwt是Python的第三方库，主要用于写Excel文件，可实现指定表单，指定单元格的写入
目前仅支持 xls  不支持 xlsx

import xlwt

创建工作簿
work_bk = xlwt.Workbook()
创建表
worksheet = work_bk.add_sheet('sheet1')
往单元格写入内容
worksheet.write(0,0,label='row 0 ,column 0 value')
保存
work_bk.save('1.xls')



追加方式写Excel
在你原有不变的情况下，给你追加来写入
依赖Python的 xlutils

pip install xlutils == 2.0.0   建议装这个版本的


xlutils 是通过复制一个副本进行操作后保存一个新文件，xlutils像是 xlrd xlwt 库之间的一座桥梁
xlutils 是依赖xlrd  xlwt 2个库的
建议先装上面2个库，再来装xlutils


xlutils 你可以在原来的基础上修改，也可以另存为新的一份

导入模块
from xlutils.copy import copy
import xlrd

打开旧的Excel文件
oldweb = xlrd.open_workbook(excelfile,formating_info=True)
复制旧的
newweb = copy(oldweb)
获取你计划修改的表格
newws = newweb.get_sheet(0)

修改对应的行列内容
newws.write(rownum,colnum,value)

保存
newwb.save(excelfile)

保持一致的excelfile 就覆盖，不一样的就新建


这个write  如果目标无值 那就写入  有的话 那就修改为当前想输入的值




日常使用场景--
1.接口自动化的数据驱动     一个接口 多个参数组合   调用多次接口

2.点餐项目批量构造数据

批量上传需要大量的类目，验证分页功能是否正常以及数量的上限



对于1举例

import xlrd

data = xlrd.open_workbook('1.xlsx')
table = data.sheet_by_name('1')
nrows = table.nrows

def check_user(username,passwd)
    if username ==  'admin':
        if passwd == '123456'
            return 'yes'
        else:
            return 'no'
    else:
        return 'no no'
        
        

for i in range(1,nrows):
    row_v = table.row_values(n)
    result = check_user(row_v[0],row_v[1])
    print(result)
    
    
    


从excel 来获取数据，驱动用例，这样如果用户名密码变了，那么我们只需要更新Excel即可
比如说你后期的用例都要登录，那你登录如果写到100个用例，都是写死的，如果密码变了就很难受，
你放到Excel中去，那么只需要变更Excel的数据即可




场景2  就是你结合for循环来一个write

"""

# def hello_father():
#     def decorator(fun):
#         def wrapper():
#             print('开始')
#             fun()
#             print('结束')
#         return wrapper
#     return decorator
#
#
# @hello_father()
# def hello():
#     print('hello world')
#
#
# hello()



# def hello_father(fun):
#     def wrapper():
#         print('do something')
#         fun()
#         print('you are win')
#     return wrapper
#
#
#
# @hello_father
# def hello():
#         print('fighting! lx')
#
#
# hello()